package com.du.method;


import com.du.config.*;
import com.du.entity.ColumnInfo;
import com.du.entity.MysqlInfo;

import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Desc : mysql转化类
 * @Author: shuangdu
 * @date : 2021/3/26
 */
public abstract class MysqlConvert {

    // 模板字段
    public static final Map<String, List<String>> templateFieldMap = new HashMap<>();

    private String tableSql;

    private String template;

    private MysqlConvertConfig config;

    static {
        Class<MysqlRule.RuleInfo> infoClass = MysqlRule.RuleInfo.class;
        Field[] fields = infoClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(MapInfo.class)) {
                MapInfo mapInfo = field.getAnnotation(MapInfo.class);
                List<String> col;
                if (templateFieldMap.containsKey(mapInfo.sign())) {
                    col = templateFieldMap.get(mapInfo.sign());
                } else {
                    col = new ArrayList<>();
                }
                col.add(field.getName());

                templateFieldMap.put(mapInfo.sign(), col);
            }
        }
    }

    public void initialize(String tableSql, String template) {
        this.tableSql = tableSql;
        this.template = template;
        this.config = new MysqlConvertConfig();
    }

    public String getTableSql() {return this.tableSql;}

    public String getTemplate() {return this.template;}

    public MysqlConvertConfig getConfig() {return this.config;}

    /**
     * 将table语句转换为java对象
     * @return
     */
    public MysqlInfo generateInfo() {
        MysqlInfo info = new MysqlInfo();
        String[] baseParagraph = this.tableSql.split("\n");
        MysqlRule.RuleInfo ruleInfo = this.config.ruleInfo();

        List<ColumnInfo> columnInfoList = new ArrayList<>();
        List<String> primaryKeyList = new ArrayList<>(2);
        List<String> uniqueKeyList = new ArrayList<>(8);
        List<String> indexKeyList = new ArrayList<>(8);
        boolean head = false;
        for (String paragraph : baseParagraph) {
            System.out.println(paragraph);
            if (!head) {
                // 表信息
                String tableName = match(paragraph, ruleInfo.getTableName(), str -> replaceColumn(str.replaceAll(MysqlRule.TABLE_NAME_PREFIX, "")));
                if (tableName != null) {
                    head = true;
                    info.setTableName(tableName.substring(0, tableName.length() - 1));
                }
                continue;
            }

            String s = paragraph.toLowerCase();

            // 匹配字段
            String column = match(s, ruleInfo.getColumnName(), this::replaceColumn);
            if (column != null) {
                ColumnInfo columnInfo = new ColumnInfo();
                // 字段类型
                String columnType = match(s, ruleInfo.getColumnType(), (str) -> str);
                // 字段类型名称
                String columnTypeName = match(s, ruleInfo.getColumnType(), (str) -> {
                    Pattern compile = Pattern.compile(ruleInfo.getColumnLength());
                    Matcher matcher = compile.matcher(str);
                    if (matcher.find()) {
                        String columnLength = matcher.group();
                        columnInfo.setColumnLength(columnLength.substring(1, columnLength.length() - 1));
                        return str.substring(0, str.indexOf("("));
                    }
                    return str;
                });

                // 字段类型对应 java 类型
                Class<?> javaType = this.columnJavaType(columnTypeName);

                // 字段是否为Null
                String columnIsNull = match(s, ruleInfo.getColumnIsNull(), (str) -> str);

                // 字段默认值
                String columnDefault = match(s, ruleInfo.getColumnDefault(), (str) -> {
                    String defaultVal = str.replaceAll("([ ]*default[ ]*)|([']*)", "");
                    return "null".equals(defaultVal) ? "" : defaultVal;
                });

                // 字段备注
                String columnComment = match(s, ruleInfo.getColumnComment(), str -> {
                    return str.substring(str.indexOf("'") + 1, str.length() - 1);
                });


                // 放入字段信息实体类
                columnInfo.setColumnName(column);
                columnInfo.setColumnType(columnType);
                columnInfo.setColumnTypeName(columnTypeName);
                columnInfo.setColumnJavaTypeName(javaType == null ? "" : javaType.getName());
                columnInfo.setColumnJavaTypeSimpleName(javaType == null ? "" : javaType.getSimpleName());
                columnInfo.setColumnIsNull(columnIsNull == null ? "是" : "否");
                columnInfo.setColumnDefault(columnDefault);
                columnInfo.setColumnComment(columnComment);
                columnInfoList.add(columnInfo);

                continue;
            }

            // 主键
            match(s, ruleInfo.getPrimaryKey(), (str) -> {
                addKeyList(str, primaryKeyList);
                return null;
            });

            // 外键
            match(s, ruleInfo.getUniqueKey(), (str) -> {
                addKeyList(str, uniqueKeyList);
                return null;
            });

            // 索引
            match(s, ruleInfo.getIndexKey(), (str) -> {
                addKeyList(str, indexKeyList);
                return null;
            });

            s = paragraph;
            String tableType = match(s, ruleInfo.getTableType(), (str -> str.replaceAll(MysqlRule.TABLE_TYPE_PREFIX, "")));
            String tableCharset = match(s, ruleInfo.getTableCharset(), (str -> str.replaceAll(MysqlRule.TABLE_CHARSET_PREFIX, "")));
            String tableDesc = match(s, ruleInfo.getTableDesc(), (str -> str.replaceAll(MysqlRule.TABLE_DESC_PREFIX, "")));
            info.setTableType(tableType);
            info.setTableCharset(tableCharset);
            info.setTableDesc(tableDesc);

//            System.out.println(paragraph);
        }

        // set
        changeKey(columnInfoList, primaryKeyList, uniqueKeyList, indexKeyList);

        info.setColumns(columnInfoList);
        info.setPrimaryKey(primaryKeyList);
        info.setUniqueKey(uniqueKeyList);
        info.setIndexKey(indexKeyList);
        return info;
    }

    /**
     * 字段对应的java类型
     * @param columnType
     * @return
     */
    private Class<?> columnJavaType(String columnType) {
        return MysqlJavaTypeMatch.match(columnType);
    }

    /**
     * 匹配
     * @param str
     * @param regex
     * @param handle
     * @return
     */
    private String match(String str, String regex, MatchFieldHandle handle) {
        Pattern compile = Pattern.compile(regex);
        Matcher matcher = compile.matcher(str);
        if (matcher.find()) {
            return handle.process(matcher.group());
        }
        return null;
    }

    /**
     * 放入数组
     * @param str
     * @param list
     */
    private void addKeyList(String str, List<String> list) {
        String keyStr = str.substring(str.indexOf("(") + 1, str.length() - 1);
        if (keyStr.contains(MysqlRule.KEY_SPLIT)) {
            String[] keys = keyStr.split(MysqlRule.KEY_SPLIT);
            for (String key : keys) {
                String subKey = replaceColumn(key);
                System.out.println("subKey: " + subKey);
                list.add(subKey);
            }
        } else {
            list.add(replaceColumn(keyStr));
        }
    }
    private void changeKey(List<ColumnInfo> columnInfoList, List<String> primaryList, List<String> uniqueList, List<String> indexList) {
        for (ColumnInfo info : columnInfoList) {
            String name = info.getColumnName();
            if (primaryList.contains(name)) {
                info.setPrimaryKey("主键");
            }
            if (uniqueList.contains(name)) {
                info.setUniqueKey("外键");
            }
            if (indexList.contains(name)) {
                info.setIndexKey("索引");
            }
        }
    }
    private String replaceColumn(String source) {
        return source.replaceAll(MysqlRule.COL_WRAP_REGEX, "");
    }

    public abstract String parse(MysqlInfo info);
}
